﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using Medora.DataContract;

namespace Medora.Data
{
    public class AsyncRepository<T> : IAsyncRepository<T> where T : class
    {
        /// <summary>
        /// Adds the save.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public async void AddSave(T entity)
        {
            using (var ctx = new MedoraDbContext())
            {
                var dbSet = ctx.Set<T>();
                dbSet.Add(entity);
                
                int savedCount=await ctx.SaveChangesAsync();
            }
            
            
        }

        /// <summary>
        /// Updates the save.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public async void UpdateSave(T entity)
        {
            using (var ctx = new MedoraDbContext())
            {
                var dbSet = ctx.Set<T>();
                DbEntityEntry dbEntityEntry = ctx.Entry(entity);
                if (dbEntityEntry.State == EntityState.Detached)
                {
                    dbSet.Attach(entity);
                }
                dbEntityEntry.State = EntityState.Modified;
                int savedCount = await ctx.SaveChangesAsync();
            }


        }

        /// <summary>
        /// Called when [field update save].
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="field">The field.</param>
        public async void OneFieldUpdateSave(T entity, Expression<Func<T, int>> field)
        {
            using (var ctx = new MedoraDbContext())
            {
                var dbSet = ctx.Set<T>();
                DbEntityEntry dbEntityEntry = ctx.Entry(entity);
                if (dbEntityEntry.State == EntityState.Detached)
                {
                    dbSet.Attach(entity);
                    ctx.Entry(entity).Property(field).IsModified = true;
                }
                //dbEntityEntry.State = EntityState.Modified;
                int savedCount = await ctx.SaveChangesAsync();
            }
        }


        /// <summary>
        /// Gets the by id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns>`0.</returns>
        public T GetById(Guid id)
        {
            using (var ctx = new MedoraDbContext())
            {
                var dbSet = ctx.Set<T>();
                return dbSet.Find(id);
            }
            
        }

        /// <summary>
        /// Gets the specified filter.
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <param name="orderBy">The order by.</param>
        /// <param name="includeProperties">The include properties.</param>
        /// <returns>IEnumerable{`0}.</returns>
        public IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
        {
            using (var ctx = new MedoraDbContext())
            {
                IQueryable<T> query = ctx.Set<T>();

                if (filter != null)
                {
                    query = query.Where(filter);
                }

                foreach (var includeProperty in includeProperties.Split
                    (new char[] {','}, StringSplitOptions.RemoveEmptyEntries))
                {
                    query = query.Include(includeProperty);
                }

                if (orderBy != null)
                {
                    return orderBy(query).ToList();
                }
                else
                {
                    return query.ToList();
                }
            }
        }


        /// <summary>
        /// Gets the specified filter.
        /// </summary>
        /// <param name="takeSize">Size for top</param>
        /// <param name="filter">The filter.</param>
        /// <param name="orderBy">The order by.</param>
        /// <param name="includeProperties">The include properties.</param>
        /// <returns>IEnumerable{`0}.</returns>
        public IEnumerable<T> GetTake(int takeSize, Expression<Func<T, bool>> filter = null, Func<IQueryable<T>,  IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
        {
            using (var ctx = new MedoraDbContext())
            {
                IQueryable<T> query = ctx.Set<T>();

                if (filter != null)
                {
                    query = query.Where(filter);
                }

                foreach (var includeProperty in includeProperties.Split
                    (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    query = query.Include(includeProperty);
                }

                if (orderBy != null)
                {
                    return orderBy(query).Take(takeSize).ToList();
                }
                else
                {
                    return query.Take(takeSize).ToList();
                }
            }
        }

        
    }
}